﻿using System;
using System.Collections.Generic;
using System.Windows;
using Bookvar.ObjectModel;
using Bookvar.UI.Arrangers;


namespace Bookvar.UI.Arrangers
{
    public class MindmapTopicArranger : TopicArranger
    {
        private struct Dimensions
        {
            public readonly double RightHeight;
            public readonly double LeftHeight;

            public Dimensions(double rightHeight, double leftHeight)
            {
                RightHeight = rightHeight;
                LeftHeight = leftHeight;
            }
        }
        private const int XOffset = 50;
        private const int YOffset = 5;

        private readonly List<TopicContentPresenter> newPresenters = new List<TopicContentPresenter>();

        #region TotalHeight

        private static readonly DependencyProperty TotalHeightProperty =
            DependencyProperty.Register( "TotalHeight", typeof( double ), typeof( MindmapTopicArranger ) );

        public static void SetTotalHeight( DependencyObject element, double value )
        {
            element.SetValue( TotalHeightProperty, value );
        }

        public static double GetTotalHeight( DependencyObject element )
        {
            return (double) element.GetValue( TotalHeightProperty );
        }

        #endregion

        #region ChildrenTotalHeight

        public static readonly DependencyProperty ChildrenTotalHeightProperty =
            DependencyProperty.Register( "ChildrenTotalHeight", typeof( double ), typeof( MindmapTopicArranger ) );

        public static void SetChildrenTotalHeight( DependencyObject element, double value )
        {
            element.SetValue( ChildrenTotalHeightProperty, value );
        }

        public static double GetChildrenTotalHeight( DependencyObject element )
        {
            return (double) element.GetValue( ChildrenTotalHeightProperty );
        }

        #endregion

        public MindmapTopicArranger( MindMapPanel panel ) : base( panel )
        {
        }

        protected internal override void ArrangeMainTopic( TopicContentPresenter mainTopicPresenter, Size size )
        {
            SetLeft( mainTopicPresenter, ( size.Width - mainTopicPresenter.ActualWidth ) / 2 );
            SetTop( mainTopicPresenter, ( size.Height - mainTopicPresenter.ActualHeight ) / 2 );

            FindNewTopics( mainTopicPresenter.Children );

            foreach ( TopicContentPresenter child in mainTopicPresenter.Children )
            {
                CalculateTotalHeight( child );
            }
        }

        private void FindNewTopics( IEnumerable<TopicContentPresenter> presenters )
        {
            foreach ( TopicContentPresenter child in presenters )
            {
                // Those without TotalHeight are new one
//                if ( child.ReadLocalValue( TotalHeightProperty ) == DependencyProperty.UnsetValue )
//                {
//                    newPresenters.Add( child );
//                }

                // If the position of the child is default it is a new one
                if ( child.X == 0 && child.Y == 0 )
                {
                    newPresenters.Add( child );
                }

                if ( child.Children.Count > 0 )
                {
                    FindNewTopics( child.Children );
                }
            }
        }

        private static double CalculateTotalHeight( TopicContentPresenter presenter )
        {
            double totalHeight = 0;
            if ( presenter.Children.Count == 0 )
            {
                totalHeight = presenter.DesiredSize.Height;
            }
            else if ( presenter.Children.Count == 1 )
            {
                double childrenTotalHeight = CalculateTotalHeight( presenter.Children[ 0 ] );
                SetChildrenTotalHeight( presenter, childrenTotalHeight );
                if ( childrenTotalHeight > presenter.DesiredSize.Height )
                {
                    totalHeight = childrenTotalHeight;
                }
                else
                {
                    totalHeight = presenter.DesiredSize.Height;
                }
            }
            else if ( presenter.Children.Count > 0 )
            {
                double childrenTotalHeight = 0;
                foreach ( TopicContentPresenter child in presenter.Children )
                {
                    childrenTotalHeight += CalculateTotalHeight( child ) + YOffset;
                }
                SetChildrenTotalHeight( presenter, childrenTotalHeight );
                if ( childrenTotalHeight > presenter.DesiredSize.Height )
                {
                    totalHeight = childrenTotalHeight;
                }
                else
                {
                    totalHeight = presenter.DesiredSize.Height;
                }
            }

            SetTotalHeight( presenter, totalHeight );
            return totalHeight;

        }
        protected override void BalanceMap(TopicContentPresenter presenter)
        {
            if (presenter.Topic is MainTopic)
            {
                int halfChildrenCount = presenter.Children.Count/2;

                for (int i = 0; i < presenter.Children.Count; i++)
                {
                    TopicContentPresenter child = presenter.Children[i];
                    child.GrowingDirection = (i < halfChildrenCount ? GrowingDirection.Right : GrowingDirection.Left);
                    BalanceMap(child);
                }
            }
            else
            {
                foreach (TopicContentPresenter child in presenter.Children)
                {
                    child.GrowingDirection = GrowingDirection.Unset;
                    BalanceMap(child);
                }
            }
        } 
        private Dimensions ArrangeMainTopicChildren(TopicContentPresenter parentPresenter)
        {
            double totalRightHeight = 0;
            double totalLeftHeight = 0;

            // Set growing directions
//            if (IsBalance)
//            {
//               BalanceMap(parentPresenter);
//            }
//            else
//            {
            foreach (TopicContentPresenter newPresenter in newPresenters)
            {
                TopicContentPresenter newPresenterParent = newPresenter.ParentTopicPresenter;
                    
                if ( newPresenterParent != null && newPresenterParent.Topic.IsMainTopic )
                {
                    int newPresenterIndex = newPresenterParent.Children.IndexOf(newPresenter);
                    if ( newPresenterIndex > 0 )
                    {
                        // If there are more than one child take the growing direction from the previous sibling
                        newPresenter.GrowingDirection =
                            newPresenterParent.Children[ newPresenterIndex - 1 ].GrowingDirection;
                    }
                    else
                    {
                        // If it is the only topic on level 1, default growing is right
                        newPresenter.GrowingDirection = GrowingDirection.Right;
                    }
                }
//                }
            }
            // Calculate dimensions
            foreach (TopicContentPresenter childPresenter in parentPresenter.Children)
            {
                switch (childPresenter.GrowingDirection)
                {
                    case GrowingDirection.Left:
                    {
                        totalLeftHeight += GetTotalHeight(childPresenter);
                        break;
                    }
                    case GrowingDirection.Right:
                    {
                        totalRightHeight += GetTotalHeight(childPresenter);
                        break;
                    }
                    default:
                    {
                        throw new InvalidOperationException("Invalid GrowingDirection.");
                    }
                }
            }

            newPresenters.Clear();

            return new Dimensions(totalRightHeight, totalLeftHeight);
        }

        private static Dimensions ArrangeTopicChildren(TopicContentPresenter parentPresenter)
        {
            double totalRightHeight = 0;
            double totalLeftHeight = 0;

            switch (parentPresenter.GrowingDirection)
            {
                case GrowingDirection.Left:
                    totalLeftHeight = GetTotalHeight(parentPresenter);
                    break;
                case GrowingDirection.Right:
                    totalRightHeight = GetTotalHeight(parentPresenter);
                    break;
            }

            return new Dimensions(totalRightHeight, totalLeftHeight);
        }

        protected override void ArrangeChildTopics( TopicContentPresenter parentPresenter )
        {
            if (parentPresenter.IsMoved)
            {
                double x = parentPresenter.X + parentPresenter.ActualWidth / 2;

                if (parentPresenter.ParentTopicPresenter != null)
                {
                    double parentX1 = parentPresenter.ParentTopicPresenter.X +
                                      parentPresenter.ParentTopicPresenter.ActualWidth / 2;
                    parentPresenter.GrowingDirection = (x < parentX1 ? GrowingDirection.Left : GrowingDirection.Right);
                }
            }

            Dimensions dimensions = (parentPresenter.Topic is MainTopic
                                         ? ArrangeMainTopicChildren(parentPresenter)
                                         : ArrangeTopicChildren(parentPresenter));

            double parentX = GetLeft( parentPresenter );
            double parentY = GetTop( parentPresenter );

            double currentLeft = ( parentY + parentPresenter.DesiredSize.Height / 2 );
            double currentRight = ( parentY + parentPresenter.DesiredSize.Height / 2 );
            if ( GetChildrenTotalHeight( parentPresenter ) >= GetTotalHeight( parentPresenter ) )
            {
                currentLeft -= dimensions.LeftHeight / 2;
                currentRight -= dimensions.RightHeight / 2;
            }
            else
            {
                currentLeft -= GetChildrenTotalHeight( parentPresenter ) / 2;
                currentRight -= GetChildrenTotalHeight( parentPresenter ) / 2;
            }
            

            #region moved topic logic

//calculate how moving affects the height
//            for ( int i = 0; i < parentPresenter.Children.Count; i++ )
//            {
//                TopicContentPresenter presenter = parentPresenter.Children[ i ];
//                if ( presenter.IsMoved )
//                {
//                    double totalHeight = GetTotalHeight( presenter );
//                    switch (GetGrowingDirection( presenter ))
//                    {
//                        case GrowingDirection.Left:
//                        {
//                            double positionX = currentLeft + ( totalHeight / 2 );
//                            double offset = Math.Abs( GetTop( presenter ) - positionX );
//
//                            currentLeft += totalHeight + YOffset + offset;
//
//                            totalLeftHeight += offset;
//                            SetTotalHeight( presenter, totalHeight + offset );
//
//                            break;
//                        }
//                        case GrowingDirection.Right:
//                        {
//                            double positionX = currentRight + ( totalHeight / 2 );
//                            double offset = Math.Abs( GetTop( presenter ) - positionX );
//                            
//                            currentRight += totalHeight + YOffset + offset; 
//                            totalRightHeight += offset;
//                            SetTotalHeight( presenter, totalHeight + offset );
//                            break;
//                        }
//                        default:
//                        {
//                            throw new InvalidOperationException( "Invalid GrowingDirection." );
//                        }
//                    }
//                }
//            }
            //reset counters
//            currentLeft = parentY - ( totalLeftHeight / 2 );
//            currentRight = parentY - ( totalRightHeight / 2 );

            #endregion

            for ( int i = 0; i < parentPresenter.Children.Count; i++ )
            {
                TopicContentPresenter presenter = parentPresenter.Children[ i ];

                Point childPoint;
                
                switch (presenter.GrowingDirection)
                {
                    case GrowingDirection.Left:
                    {
                        childPoint =
                            new Point( parentX - XOffset - presenter.DesiredSize.Width, currentLeft + ( GetTotalHeight( presenter ) / 2 ) );
                        currentLeft += GetTotalHeight( presenter ) + YOffset; 

                        break;
                    }
                    case GrowingDirection.Right:
                    {
                        childPoint =
                            new Point( parentX + parentPresenter.DesiredSize.Width + XOffset, currentRight + ( GetTotalHeight( presenter ) / 2 ) );
                        currentRight += GetTotalHeight( presenter ) + YOffset; 
                        break;
                    }
                    default:
                    {
                        throw new InvalidOperationException( "Invalid GrowingDirection." );
                    }
                }

                if ( !presenter.IsMoved )
                {
                    SetLeft( presenter, childPoint.X );
                    SetTop( presenter, childPoint.Y - presenter.DesiredSize.Height / 2);
                }
            }

            base.ArrangeChildTopics( parentPresenter );
        }

    }
}